package com.bite.common.security.service;

import cn.hutool.core.lang.UUID;
import com.bite.common.core.constants.CacheConstants;
import com.bite.common.core.constants.JwtConstants;
import com.bite.common.redis.service.RedisService;
import com.bite.common.core.domain.LoginUser;
import com.bite.common.core.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

//操作用户登录token的方法
@Service
@Slf4j
public class TokenService {

    @Autowired
    public RedisService redisService;

    public  String createToken(Long userId,String secret,Integer identity,String nickName,String headImage){
        Map<String,Object> claims=new HashMap<>();
        String userKey= UUID.fastUUID().toString();
        claims.put(JwtConstants.LOGIN_USER_ID,userId);
        claims.put(JwtConstants.LOGIN_USER_KEY,userKey);
        String token= JwtUtils.createToken(claims,secret);
        String tokenkey=getTokenKey(userKey); //假如发现重复统一用函数
//        String key= CacheConstants.LOGIN_TOKEN_KEY +userKey;//拼一个前缀方便统一管理
        LoginUser loginUser=new LoginUser();
        loginUser.setNickName(nickName);
        loginUser.setIdentity(identity);
        loginUser.setHeadImage(headImage);
        //过期时间的处理，720分钟-12个小时,用户的竞赛会参加 (2-3h)
        redisService.setCacheObject(tokenkey,loginUser,CacheConstants.EXP, TimeUnit.MINUTES);
        //假如用户拿着过期的token,我们需要判断什么是否过期，通过redis是否过期来判断。JWT上加一个参数
       return token;

    }
    //延长token的有效时间，就是延长redis中用于用户身份认证的敏感信息的有效时间，操作redis
    //通过token拿到唯一标识
    //身份认证之后，才会去调用，并且要在转发业务逻辑之前来去延长，这样你中途业务执行时候就过期了，所以最好就是业务执行逻辑之前

    public void extendToken(Claims claims){
//        String userKey = JwtUtils.getUserKey(claims); //获取jwt中的key
           String userKey=getUserKey(claims);
           if(userKey==null){
               return;
           }
        String tokenKey= getTokenKey(userKey);
        //一般情况会判断一下，假如720min-12小时，假如刚走1h，就延长就没必要了，找一个临界值，比如还剩2-3h，3h延长
        Long expire=redisService.getExpire(tokenKey,TimeUnit.MINUTES);
        if(expire!=null&&expire<CacheConstants.REFRESH_TIME){
            redisService.expire(tokenKey,CacheConstants.EXP,TimeUnit.MINUTES);
        }
    }
    private String getTokenKey(String userKey){
       return  CacheConstants.LOGIN_TOKEN_KEY +userKey;//拼一个前缀方便统一管理
    }

    public LoginUser getLoginUser(String token,String secret) {
        String userKey=getUserKey(token,secret);
        if(userKey==null){return null;}
       return redisService.getCacheObject(getTokenKey(userKey),LoginUser.class);
    }
    public  Long getUserId(Claims claims){
            if (claims == null) {return null;}
        return  Long.valueOf(JwtUtils.getUserId(claims)); //获取jwt中的key
    }
    public String getUserKey(Claims claims){
        if (claims == null) {return null;}
        return JwtUtils.getUserKey(claims); //获取jwt中的key
    }

    public String getUserKey(String token,String secret){
        Claims claims=getClaims(token,secret);
        if (claims == null) {return null;}
        String userKey = JwtUtils.getUserKey(claims); //获取jwt中的key
        return userKey;
    }
    public  Claims getClaims(String token,String secret){
        Claims claims;
        try {
            //payload存着用户的敏感信息，比如唯一标识信息
            claims = JwtUtils.parseToken(token, secret); //获取令牌中信息 解析payload中信息
            //下面是处理假如解析出错的逻辑处理
            if (claims == null) {
                //todo 走到延长的一步，说明身份认证过了，就基本没有问题了，出于安全角度(另外我的身份认证已经过了，
                // 出于好心我去给他延长，但是假如延长错了，我不能直接就说他有问题，我不给他延长了不就好了。
                log.error("解析token: {}出现异常",token);
                return null;
            }
        } catch (Exception e) {
            log.error("解析token: {}出现异常",token,e);
            //todo
            return null;
        }
        return claims;
    }

    public boolean deleteLoginUser(String token, String secret) {
        String userKey=getUserKey(token,secret);
        if(userKey==null)return false;
        return redisService.deleteObject(getTokenKey(userKey));
    }

    public  void refreshLoginUser(String nickName,String headImage,String userKey){
        String tokenKey=getTokenKey(userKey);
        LoginUser loginUser= redisService.getCacheObject(tokenKey,LoginUser.class);
        loginUser.setNickName(nickName);
        loginUser.setHeadImage(headImage);
        redisService.setCacheObject(tokenKey,loginUser);
    }







}
